#!/usr/bin/php -q
<?
error_reporting(E_ALL ^ E_NOTICE);
/*
 * gen_fun.php
 * This file is part of PHP_ExtGenerator
 *
 * Copyright (C) 2008 - Dimitri Giardina
 *
 * PHP_ExtGenerator is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PHP_ExtGenerator is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PHP_ExtGenerator; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

 	function simplexml_append(SimpleXMLElement &$parent, SimpleXMLElement &$new_child){
		$node1 = dom_import_simplexml($parent);
		$dom_sxe = dom_import_simplexml($new_child);
		$node2 = $node1->ownerDocument->importNode($dom_sxe, true);
		$node1->appendChild($node2);

	}
	function simplexml_remove(SimpleXMLElement &$p) {
		$o=dom_import_simplexml($p);
		$o->parentNode->removeChild($o);
	}

	function simplexml_recurse_file(&$r) {
		//$root=$r->children();
		$root=$r;
		foreach($r->children() as $node) {
			$type=$node->getName();
			if ($type=='include') {
				$n=$node; // rcopy.
				$src=$node["file"];
				if (isset($node["type"]))
					$exp=$node["type"];
				else
					$exp="normal";
				if (file_exists($src)) {
					switch($exp) {
					case "normal":
						$n1=simplexml_load_file((string)$src);
						simplexml_append($root,$n1);
						break;
					case "multiple":
						$n1=simplexml_load_file((string)$src);
						foreach($n1->children() as $nod2)
							simplexml_append($root,$nod2);
						break;
					}
					//$r->addChild($n);
//					simplexml_remove($node);
				} else die("$src not exists");
			} else
				simplexml_recurse_file(&$node);
		}
	}
 	function load_xml_file($file) {
 		$p=simplexml_load_file($file);
 		simplexml_recurse_file(&$p);
 		return $p;

 	}

	function gen_reso_code($phase,$glob) {
		$resources=$glob->resources[0];
		$extname=$glob->info->name["value"];
		$extnameclean=substr($extname,4);
		switch($phase) {
		case 1:
			foreach ( $resources->resource as $r) {
				echo "static int le_".$r["name"].";\n";
				echo "#define le_".$r["name"]."_name \"".$r["descr"]."\"\n";
			}
			foreach($resources->condition as $c) {
				echo "#ifdef ".$c["value"]."\n";
				foreach($c->resource as $r) {
					echo "static int le_".$r["name"].";\n";
					echo "#define le_".$r["name"]."_name \"".$r["descr"]."\"\n";
				}
				echo "#endif\n";
			}

			break;
		case 2:
			foreach ( $resources->resource as $r ) {
				$destructorcode=$r->destructor;
				echo "static void _".$extnameclean."_".$r["name"]."(zend_rsrc_list_entry *rsrc TSRMLS_DC) {\n$destructorcode}\n";
			}
			foreach($resources->condition as $c) {
				echo "#ifdef ".$c["value"]."\n";
				foreach ( $c->resource as $r ) {
					$destructorcode=$r->destructor;
					echo "static void _".$extnameclean."_".$r["name"]."(zend_rsrc_list_entry *rsrc TSRMLS_DC) {\n$destructorcode}\n";
				}
				echo "#endif\n";
			}

			break;
		case 3:
			foreach ( $resources->resource as $r ) {
				echo "\tle_".$r["name"]."=zend_register_list_destructors_ex(_${extnameclean}_".$r["name"].",NULL,le_".
					$r["name"]."_name, module_number);\n";
			}
			foreach($resources->condition as $c) {
				echo "#ifdef ".$c["value"]."\n";
				foreach ( $c->resource as $r ) {
					echo "\tle_".$r["name"]."=zend_register_list_destructors_ex(_${extnameclean}_".$r["name"].",NULL,le_".
						$r["name"]."_name, module_number);\n";
				}
				echo "#endif\n";
			}

			break;
		}

	}
	function conv_type($k,$p=false) {
		if ($p) return "z";
		switch($k) {
		case "void":
			break;
		case "int":
		case "long":
			return "l";
			break;
		case "float":
			return "d";
			break;
		case "string":
		case "char *":
			return "s";
			break;
		case "char":
			return "c";
			break;
		case "array":
			return "a";
			break;
		case "resource":
			return "r";
			break;
		case "zval":
			return "z"; break;
		}
	}
	function conv_cont($a,$b,$ptr=false) {
		if ($ptr) {
			return "&".$a; break;
		} else {
			switch($b) {
			case "char":
			case "int":
			case "long":
			case "float":
				return "&$a"; break;
			case "string":
			case "char *":
				return "&$a,&${a}_length"; break;
			case "array":
				return "$a"; break;
			case "resource":
				return "$a"; break;
			case "zval":
				return "$a"; break;
			}
		}
	}
	function conv_typec($x,$ptr=false) {
		if ($ptr) {
			return "zval *";
/*			switch($x) {
			case "int": return "int ";
			case "long": return "long ";
			case "float": return "float ";
			}
*/
		} else {
			switch($x) {
			case "char": return "char ";
			case "int": return "int ";
			case "long": return "long ";
			case "float": return "float ";
			case "array": return "zval **";
			case "resource": return "zval **";
			case "string": return "char *";
			case "zval": return "zval ";
			}
		}
	}
	function conv_funpara($a,&$arg) {
/*
		print_r($arg);
		print_r($a);
*/
		$ptr=$a[1]{0};
		if ($ptr=="*") {
			$arg["name"]=substr($a[1],1);
			$arg["convtype"]=conv_type($a[0],true);
			$arg["convcont"]=conv_cont($arg["name"],$a[0],true);
			$arg["type"]=conv_typec($a[0],true);
/*print_r($a); print_r($arg);
			echo "[".substr($a[1],1)."]\n\n";
die("");
*/
		} else {
			$arg["name"]=$a[1];
			$arg["convtype"]=conv_type($a[0]);
			$arg["convcont"]=conv_cont($arg["name"],$a[0]);
			$arg["type"]=conv_typec($a[0]);
		}
	}

	function gen_parse_fun($v,$f) {
		$x=explode("(",$v);

		$x1=$x[0];
		$x2=str_replace(")","",$x[1]);
		$ret=explode(" ",$x1);
		if ($f->def!="") {
		    $def=$f->def."\n";
		    if ($def[0]=="\n") {
                $def=substr($def,1);
            }
		} else $def="";
		if ($f->code!="") {
		    $code="".$f->code;
            if ($code[0]=="\n") {
                $code=substr($code,1);
            }
            $code=rtrim($code,"\n\t\s");
		} else
		    $code="php_error(E_WARNING, \"function %s() not implemented\",get_active_function_name(TSRMLS_C));";
		if ($x2!="void" && $x2!="") {
			$args1=explode(",",$x2);
			for($i=0;$i<sizeof($args1);$i++){
				$a=explode(" ",trim($args1[$i]));
				$ptr=$a[1]{0};
				conv_funpara($a,&$arg);
				$args[]=$arg;
			}
			// generate var containers.
			foreach ($args as $x) {
				echo "\t".$x["type"].$x["name"].";\n";
				if ($x["convtype"]=='s') echo "\tint ".$x["name"]."_length;\n";
				$l.=$x["convtype"];
				if ($l2!="") $l2.=",";
				$l2.=$x["convcont"];
			}
			if (count($args)>0) echo "\n";
			// generate parse code.
			$tmpl= <<<EOF
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "$l", $l2)==FAILURE) {
	    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error passing parameters for {$ret[1]}");
	}
EOF;
			echo $def.$tmpl."\n\n".$code;
		} else {
		    // generate parse code.
		    echo $def.$code;
		}
	}
	function gen_m4($glob) {
		$extname=$glob->info->name["value"];
		$extnameclean=substr($extname,4);
		$m4adds=$glob->info->m4adds;
		$list_c_sources=$extname.".c";
		$extname1=strtoupper($extnameclean);
		$tmpl=<<<EOF
PHP_ARG_ENABLE($extnameclean,
  [ whattawhatta ],
  [ --with-$extnameclean        Enable "$extnameclean" extension support])

if test \$PHP_${extname1} != "no"; then
	PHP_SUBST({$extname1}_SHARED_LIBADD)
	PHP_NEW_EXTENSION(${extnameclean}, ${list_c_sources}, \$ext_shared)

$m4adds
fi
EOF;
		echo $tmpl;
	}
	function gen_fun_code($phase,$glob) {
		$functions=$glob->functions[0];
		$conditions=$glob->functions[0]->conditions[0];
		$extname=$glob->info->name["value"];
		$extnameclean=substr($extname,4);
		switch($phase) {
		case 1:
			// scrivo tutte le  funzioni.
			foreach($functions->function as $f) {
				echo "PHP_FUNCTION(".$f["name"].") {\n";
				gen_parse_fun($f->def["value"],$f);
				echo "\n}\n";
			}
			foreach($functions->condition as $c) {
				echo "#ifdef ".$c["value"]."\n";
				foreach($c->function as $f) {

					echo "PHP_FUNCTION(".$f["name"].") {\n";
					gen_parse_fun($f->def["value"],$f);
					echo "\n}\n";
				}
				echo "#endif\n";
			}
			break;
		case 2:
			echo "static function_entry php_${extnameclean}_functions[] = {\n";
			foreach ($functions->function as $f) {
				echo "\tPHP_FE(".$f["name"].",\tNULL)\n";
			}
			foreach($functions->condition as $c) {
				echo "/* condition */\n";
				echo "#ifdef ".$c["value"]."\n";
				foreach($c->function as $f) {
					echo "\tPHP_FE(".$f["name"].",\tNULL)\n";
				}
				echo "#endif\n";
				echo "/* end condition */\n";
			}

			echo "\t{ NULL, NULL, NULL }\n};\n";

			break;
		case 3:
			// scrivo le dichiarazioni delle funzioni.
			foreach($functions->function as $f) {
				echo "PHP_FUNCTION(".$f["name"].");\n";
			}
			foreach($functions->condition as $c) {
				echo "#ifdef ".$c["value"]."\n";
				foreach($c->function as $f) {
					echo "PHP_FUNCTION(".$f["name"].");\n";
				}
				echo "#endif\n";
			}
			break;

		}
	}
	function gen_constants($glob) {
		$constants=$glob->constants[0];
		$extname=$glob->info->name["value"];
		$types=array("l"=>"LONG");
		foreach ( $constants->const as $r) {
			$t=(string)$r["type"];
			$type=$types[$t];
			echo "\tREGISTER_${type}_CONSTANT(\"".$r["name"]."\",".$r["value"].",".$r["flags"].");\n";
		}
		foreach($constants->condition as $c) {
			echo "\t#ifdef ".$c["value"]."\n";
			foreach($c->const as $r) {
				$t=(string)$r["type"];
				$type=$types[$t];
				echo "\t\tREGISTER_${type}_CONSTANT(\"".$r["name"]."\",".$r["value"].",".$r["flags"].");\n";
			}
			echo "\t#endif\n";
		}
	}
	function gen_header($glob,$pp) {
		$extname=$glob->info->name["value"];
		$extnameclean=substr($extname,4);
		$version=$glob->info->name["version"];

		$extname1=strtoupper($extnameclean);
		$codeh=$glob->codeh;
		$codeglobals=$glob->codeglobals;
		if ($pp=='pp')
		    $includePhp=<<<EOF
//#ifdef WIN32
//#include <math.h>
//#endif
//extern "C" {
#include "php.h"
#include "ext/standard/info.h"
//}
EOF;
		else
		    $includePhp=<<<EOF
#include "php.h"
EOF;
		$includeh=$glob->includeh;
		ob_start();
		gen_fun_code(3,$glob);
		$functions3=ob_get_contents();
		ob_end_clean();
		$license=gen_license($glob,"header");
		$buf=<<<EOF
$license
#ifndef PHP_${extname1}_H
/* Prevent double inclusion */
#define PHP_${extname1}_H

/* Define Extension Properties */
#define PHP_${extname1}_EXTNAME   "${extnameclean}"
#define PHP_${extname1}_EXTVER    "$version"

/* Import configure options
   when building outside of
   the PHP source tree */

/* Include PHP Standard Header */
$includePhp
$includeh

/* Define the entry point symbol
 * Zend will use when loading this module
 */

PHP_MINIT_FUNCTION(${extnameclean});
PHP_MSHUTDOWN_FUNCTION(${extnameclean});
PHP_RINIT_FUNCTION(${extnameclean});
PHP_RSHUTDOWN_FUNCTION(${extnameclean});
PHP_MINFO_FUNCTION(${extnameclean});

/*******************************
 * Function Declarations
 *******************************/
$functions3
/*******************************
 * End Function Declarations
 *******************************/

$codeh

ZEND_BEGIN_MODULE_GLOBALS(${extnameclean})
$codeglobals
ZEND_END_MODULE_GLOBALS(${extnameclean})

ZEND_DECLARE_MODULE_GLOBALS(${extnameclean})

#ifdef ZTS
#include "TSRM/TSRM.h"
#define MYGLOBALS(v)    TSRMG({$extnameclean}_globals_id, zend_${extnameclean}_globals*, v)
#else
#define MYGLOBALS(v)    ({$extnameclean}_globals.v)
#endif


extern zend_module_entry ${extnameclean}_module_entry;

#define ${extnameclean}_module_ptr &${extnameclean}_module_entry



#endif /* PHP_SAMPLE_H */
EOF;
		echo $buf;
	}
	function gen_license($glob,$part) {
		$lic=$glob->info->license;
		$extname=$glob->info->name["value"];
		$author=$glob->info->author["value"];
		if ($lic=="") return "";
		$lic=str_replace("@extname@",$extname. " extension for PHP",$lic);
		$lic=str_replace("@author@",$author,$lic);
		switch($part) {
		case "header":
			$file="php_${extname}.h";
			break;
		case "code":
			$file="${extname}.c";
			break;
		}
		$lic=str_replace("@filename@",$file,$lic);
		return $lic;
	}

	function gen_code($glob,$file,$pp) {
		$extname=$glob->info->name["value"];
		$extnameclean=substr($extname,4);
		$extname1=strtoupper($extnameclean);

		$license=gen_license($glob,"code");

        $includecpp=$glob->includecpp;
		$inclusions="#include \"$extname.h\"\n";
		ob_start();
		gen_fun_code(1,$glob);
		$functions1=ob_get_contents();
		ob_end_clean();

		ob_start();
		gen_fun_code(2,$glob);
		$functions2=ob_get_contents();
		ob_end_clean();


		ob_start();
		gen_reso_code(1,$glob);
		gen_reso_code(2,$glob);
		$resources=ob_get_contents();
		ob_end_clean();

		ob_start();
		gen_reso_code(3,$glob);
		$registerglobalsdestructors=ob_get_contents();
		ob_end_clean();

		ob_start();
		gen_constants($glob);
		$registerconstants=ob_get_contents();
		ob_end_clean();

		ob_start();
		if ($glob->code["src"]!="") {
			$f=dirname(realpath($file));
			$f1=$f."/".$glob->code["src"];
			echo "/* getting from ".$glob->code["src"]." */\n";
			echo file_get_contents($f1);
			echo "/* end getting from ".$glob->code["src"]."*/\n";
		} else {
			echo $glob->code;
		}
		$stuffcode=ob_get_contents();
		ob_end_clean();

        if ($pp=='pp')
            $getmodule=<<<EOF
//extern "C"
//{
ZEND_GET_MODULE($extnameclean)
//}
EOF;
        else
            $getmodule=<<<EOF
ZEND_GET_MODULE($extnameclean)
EOF;

        if ($extnameclean=='guichan')
            $module_deps=<<<EOF
//ZEND_MOD_REQUIRED("sdl")
EOF;
        else
            $module_deps=<<<EOF
EOF;

		ob_start();
		if ($glob->globalscode["src"]!="") {
			$f=dirname(realpath($file));
			$f1=$f."/".$glob->globalscode["src"];
			echo "/* getting from ".$glob->globalscode["src"]." */\n";
			echo file_get_contents($f1);
			echo "/* end getting from ".$glob->globalscode["src"]."*/\n";
		} else {
			echo $glob->globalscode;
		}
		$initglobals=ob_get_contents();
		ob_end_clean();


		$tmpl= <<<EOF
$license
$includecpp
// Inclusions.
$inclusions
// Globals
//ZEND_DECLARE_MODULE_GLOBALS(${extnameclean})
// End Globals

/*******************************
 * Resources
 *******************************/
$resources
/*******************************
 * End Resources
 *******************************/


/*******************************
 * Function Definitions
 *******************************/
$functions2
/*******************************
 * End Function Definitions
 *******************************/

static zend_module_dep module_deps[] = {
	$module_deps
	{NULL, NULL, NULL}
};

/*******************************
 * Standard Module Entry
 *******************************/
zend_module_entry ${extnameclean}_module_entry = {
	STANDARD_MODULE_HEADER_EX,
	NULL,
	module_deps,
	PHP_${extname1}_EXTNAME,
	php_${extnameclean}_functions, /* Functions */
	PHP_MINIT($extnameclean), /* MINIT */
	PHP_MSHUTDOWN($extnameclean), /* MSHUTDOWN */
	PHP_RINIT($extnameclean), /* RINIT */
	PHP_RSHUTDOWN($extnameclean), /* RSHUTDOWN */
	PHP_MINFO($extnameclean), /* MINFO */
	PHP_${extname1}_EXTVER,
	STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_${extname1}
$getmodule
#endif
/*******************************
 * End Standard Module Entry
 *******************************/

/*******************************
 * Init Globals
 *******************************/
$initglobals
/*******************************
 * End Init Globals
 *******************************/

/*******************************
 * PHP *_FUNCTIONS
 *******************************/
PHP_MINIT_FUNCTION($extnameclean) {
	ZEND_INIT_MODULE_GLOBALS(${extnameclean},php_${extnameclean}_init_globals,NULL);
$registerglobalsdestructors
$registerconstants
	return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION($extnameclean) {
	${extnameclean}_mshutdown();
	return SUCCESS;
}
PHP_RINIT_FUNCTION($extnameclean) {
	return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION($extnameclean) {
	return SUCCESS;
}
PHP_MINFO_FUNCTION($extnameclean) {
	php_info_print_table_start();
	php_info_print_table_header(2, "$extnameclean support", "enabled");
	php_info_print_table_end();
}
/*******************************
 * End PHP *_FUNCTIONS
 *******************************/

/*******************************
 * Stuff/Conversion Code
 *******************************/
$stuffcode
/*******************************
 * Stuff/Conversion Code
 *******************************/

/*******************************
 * Functions Code
 *******************************/
$functions1
/*******************************
 * End Functions Code
 *******************************/
EOF;
		echo $tmpl;
	}


//parse_ini_file($argv[0]);
if (sizeof($argv)>0 && file_exists($argv[1])) {
    if($argv[2]) $pp=$argv[2]; else $pp='';
	$glob=load_xml_file($argv[1]);
	$name=$glob->info->name["value"];
	ob_start();
	gen_code($glob,$argv[1],$pp);
	$buf=ob_get_contents();
	ob_end_clean();
	$fp=fopen($name.".c".$pp,"w");
	fputs($fp,$buf);
	fclose($fp);

	ob_start();
	gen_header($glob,$pp);
	$buf=ob_get_contents();
	ob_end_clean();
	$fp=fopen($name.".h","w");
	fputs($fp,$buf);
	fclose($fp);

	ob_start();
	gen_m4($glob);
	$buf=ob_get_contents();
	ob_end_clean();
	$fp=fopen("config.m4","w");
	fputs($fp,$buf);
	fclose($fp);
}
?>
